﻿using Microsoft.Extensions.Logging;
using ShardingCore.Sharding;
using ShardingCore.Sharding.Abstractions;

namespace YunQue.Core.DB.DbOption
{
    public class YunQueBaseDbContext: AbstractShardingDbContext, IShardingDbContext
    {
        private readonly string _currentUserId;
        private readonly TenantModel _tenant;
        private static ConcurrentDictionary<string, string> dataIdPerfixDic = new ConcurrentDictionary<string, string>();

        public YunQueBaseDbContext(DbContextOptions<YunQueBaseDbContext> options, UserContext userContext)
            : base(options)
        {
            _tenant = RedisWrapper.Get<TenantModel>(userContext.Token);
            if (_tenant == null) throw new Exception("租户信息不存在");
            _currentUserId = userContext.Id;
        }

        //public YunQueBaseDbContext(UserContext userContext)
        //{
        //    _tenant = RedisWrapper.Get<TenantModel>(userContext.Token);
        //    if (_tenant == null) throw new Exception("租户信息不存在");
        //    _currentUserId = userContext.Id;
        //}
        #region 数据库实体设置
        /// <summary>
        /// 连接配置
        /// </summary>
        /// <param name="optionsBuilder"></param>
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseMySql(_tenant.DBConnectionString, ServerVersion.AutoDetect(_tenant.DBConnectionString), opt =>
            {
                opt.EnableRetryOnFailure(3, TimeSpan.FromSeconds(1), new List<int>() { 4060, 10928, 10929, 40197, 40501 });
            });
#if DEBUG
            optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information);
#endif
        }
        /// <summary>
        /// 模型配置
        /// </summary>
        /// <param name="builder"></param>
        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
            //加载实体配置
            builder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
            //配置每个实体类的全局查询过滤器 只能查 IsDelete=false and TenantCode=_tenantCode 的数据
            CreateGlobalFilter(builder);
        }
        /// <summary>
        /// 保存操作
        /// </summary>
        /// <returns></returns>
        public override int SaveChanges()
        {
            OnBeforeSaving();
            return base.SaveChanges();
        }
        /// <summary>
        /// 保存操作
        /// </summary>
        /// <param name="acceptAllChangesOnSuccess"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))
        {
            OnBeforeSaving();
            return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
        }

        /// <summary>
        /// 全局过滤器
        /// </summary>
        /// <param name="modelBuilder"></param>
        private void CreateGlobalFilter(ModelBuilder modelBuilder)
        {
            foreach (var entityType in modelBuilder.Model.GetEntityTypes())
            {
                if (typeof(BaseEntity).IsAssignableFrom(entityType.ClrType))
                {
                    var clrType = entityType.ClrType;

                    var parameter = Expression.Parameter(clrType, "e");
                    var isDeletedProperty = Expression.Property(parameter, nameof(BaseEntity.IsDeleted));
                    var tenantCodeProperty = Expression.Property(parameter, nameof(BaseEntity.TenantCode));

                    var isDeletedCondition = Expression.Equal(isDeletedProperty, Expression.Constant(false));
                    var tenantCodeValue = Expression.Constant(_tenant.Code);
                    var tenantCodeCondition = Expression.Equal(tenantCodeProperty, tenantCodeValue);
                    var filterExpression = Expression.Lambda(Expression.AndAlso(isDeletedCondition, tenantCodeCondition), parameter);

                    modelBuilder.Entity(clrType).HasQueryFilter(filterExpression);
                }
            }
        }
        /// <summary>
        /// 数据操作前更改默认字段的信息
        /// </summary>
        private void OnBeforeSaving()
        {
            foreach (var entry in ChangeTracker.Entries())
            {
                var entity = entry.Entity as BaseEntity;
                switch (entry.State)
                {
                    case EntityState.Added:
                        AddEntityOption(entry);
                        break;
                    case EntityState.Modified:
                        if (entity.TenantCode != _tenant.Code) throw new Exception("无权操作该数据");
                        UpdateEntityOption(entry);
                        break;
                    case EntityState.Deleted:
                        if (entity.TenantCode != _tenant.Code) throw new Exception("无权操作该数据");
                        DeleteEntityOption(entry);
                        break;
                }
            }
        }
        /// <summary>
        /// 新增数据
        /// </summary>
        /// <param name="entry"></param>
        private void AddEntityOption(EntityEntry entry)
        {
            var tbName = entry.Metadata.GetTableName();
            string dataIdPerfix = dataIdPerfixDic.GetOrAdd(tbName, (k) =>
            {
                var type = entry.Entity.GetType();
                var attr = type.GetCustomAttributes(typeof(DataIdPrefixAttribute), false).FirstOrDefault();
                if (attr != null) return ((DataIdPrefixAttribute)attr).Prefix;
                return "";
            });
            entry.Property("Id").CurrentValue = GetPrimaryKey(dataIdPerfix);
            entry.Property("TenantCode").CurrentValue = _tenant.Code;
            entry.Property("CreateUserId").CurrentValue = _currentUserId;
            entry.Property("CreateTime").CurrentValue = DateTime.Now;
            entry.Property("IsDeleted").CurrentValue = false;
            //触发领域事件
            if (entry.Entity is IHasDomainEvents) SendEvent(entry, EntityEventType.Create);
        }
        /// <summary>
        /// 编辑数据
        /// </summary>
        /// <param name="entry"></param>
        private void UpdateEntityOption(EntityEntry entry)
        {
            entry.Property("UpdateUserId").CurrentValue = _currentUserId;
            entry.Property("UpdateTime").CurrentValue = DateTime.Now;
            //触发领域事件
            if (entry.Entity is IHasDomainEvents) SendEvent(entry, EntityEventType.Update);
        }
        /// <summary>
        /// 删除数据
        /// </summary>
        /// <param name="entry"></param>
        private void DeleteEntityOption(EntityEntry entry)
        {
            entry.State = EntityState.Modified;
            entry.Property("IsDeleted").CurrentValue = true;
            //触发领域事件
            if (entry.Entity is IHasDomainEvents) SendEvent(entry, EntityEventType.Delete);
        }
        /// <summary>
        /// 生成主键
        /// </summary>
        /// <param name="dataIdPerfix"></param>
        /// <returns></returns>
        private string GetPrimaryKey(string dataIdPerfix)
        {
            if (string.IsNullOrWhiteSpace(dataIdPerfix))
            {
                dataIdPerfix = "ID";
            }
            string key = string.Empty;
            var woker = new IdWorker(_tenant.WorkId, 1, 0);
            key = $"{dataIdPerfix}_{woker.NextId().ToString()}";
            return key;
        }
        /// <summary>
        /// 事件处理
        /// </summary>
        /// <param name="entry"></param>
        /// <param name="eventType"></param>
        private void SendEvent(EntityEntry entry, EntityEventType eventType)
        {
            var tbName = entry.Metadata.GetTableName();
            Dictionary<string, string> eventDic = new Dictionary<string, string>();
            eventDic.Add("Id", entry.CurrentValues["Id"].ToString());
            eventDic.Add("TenantCode", _tenant.Code);
            eventDic.Add("Table", tbName);
            eventDic.Add("EventType", eventType.ToString());
            eventDic.Add("UserId", _currentUserId);
            eventDic.Add("CreateTime", DateTime.Now.ToString());
            eventDic.Add("DataJson", JsonConvert.SerializeObject(entry.Entity));
            if (eventType == EntityEventType.Update)
            {
                //获取变更的字段
                var props = entry.OriginalValues.Properties;
                Dictionary<string, string> updateDic = new Dictionary<string, string>();
                foreach (var prop in props)
                {
                    var originalValue = entry.OriginalValues[prop];
                    var currentValue = entry.CurrentValues[prop];
                    if (originalValue != null && !originalValue.Equals(currentValue))
                    {
                        updateDic.Add(prop.Name, $"原始值：{originalValue?.ToString()},变更后的值：{currentValue?.ToString()}");
                    }
                }
            }
            var eventModel = new EntityEvent()
            {
                EventType = eventType,
                EntityDic = eventDic
            };
            //发送事件
            //MQ 处理事件,先假装保存到Redis
            RedisWrapper.SetAdd($"EntityEvent_{_tenant.Code}", eventModel);
        }
        #endregion

    }   
}
